// file:src/kernel/fs/fsal/fsalif.c
// autor:jiangxinpeng
// time:2021.2.26
// copyright: (C) by jiangxinpeng,All right are reserved.

#include <os/file.h>
#include <os/dir.h>
#include <os/path.h>
#include <os/diskman.h>
#include <os/fsal.h>
#include <os/fsalif.h>
#include <os/fstype.h>
#include <os/debug.h>
#include <lib/assert.h>
#include <lib/errno.h>
#include <lib/string.h>

// fsal interface
// manger all fsal operator and map to actual fs interface

static int FsalIfIncRef(int idx)
{
    fsal_file_t *fp;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    FsalFileIncRef(fp);
    return 0;
}

static int FsalIfDecRef(int idx)
{
    fsal_file_t *fp;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    FsalFileDecRef(fp);
    return 0;
}

// return file size
static size_t FsalIfFsize(int idx)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
    {
        return -1;
    }
    fp = FSAL_FILE_IDX2FILE(idx);
    fsal = fp->fsal;
    if (fsal != NULL)
        return fsal->fsize(idx);
    return -1;
}

// open file
static int FsalIfOpen(const char *path, int flag)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];
    int handler = -1;

    // clear path
    memset(new, 0, MAX_PATH_LEN);

    if (path != NULL)
    {
        // get path fsal
        fpath = FsalPathFind(path, 1);
        if (fpath != NULL)
        {
            fsal = fpath->fsal;
            if (fsal != NULL)
            {
                // switch to targe position
                if (!FsalPathSwitch(fpath, new, (char *)path))
                {
                    // open file handler
                    handler = fsal->open(new, flag);
                    FsalIfIncRef(handler);
                }
            }
        }
    }
    return handler;
}

static int FsalIfClose(int idx)
{
    fsal_file_t *fp = FSAL_FILE_IDX2FILE(idx);
    fsal_t *fsal = fp->fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    if (FSAL_FILE_BAD_FILE(fp))
        return -1;

    if (fsal != NULL)
    {
        // free file ref
        if (FsalIfDecRef(idx) < 0)
            return -EINVAL;
        // check if file ref count > 0
        if (FILE_CLOSE_NEED(fp))
        {
            return fsal->close(idx);
        }
    }
}

// set seek
static int FsalIfSeek(int idx, offset_t off, int pos)
{
    fsal_file_t *fp = FSAL_FILE_IDX2FILE(idx);
    fsal_t *fsal = fp->fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    if (FSAL_FILE_BAD_FILE(fp))
        return -1;
    if (fsal != NULL)
        return fsal->lseek(idx, off, pos);

    return -1;
}

static int FsalIfFsync(int idx)
{
    fsal_file_t *fp = FSAL_FILE_IDX2FILE(idx);
    fsal_t *fsal = fp->fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    if (FSAL_FILE_BAD_FILE(fp))
        return -1;
    if (fsal != NULL)
        return fsal->fsync(idx);

    return -1;
}

static int FsalIfOpenDir(const char *path)
{
    fsal_path_t *fpath = NULL;
    fsal_t *fsal = NULL;
    char new[MAX_PATH_LEN+1];

    if (path != NULL)
    {
        fpath = FsalPathFind(path, 1);
        if (fpath != NULL)
        {
            fsal = fpath->fsal;
            if (!FsalPathSwitch(fpath, new, (char *)path))
            {
                return fsal->opendir(new);
            }
        }
    }
    return -1;
}

static int FsalIfCloseDir(int idx)
{
    fsal_path_t *fpath = FSAL_PATH_IDX2PATH(idx);
    fsal_t *fsal = fpath->fsal;

    if (FSAL_BAD_PATH_IDX(idx))
        return -1;
    if (FSAL_BAD_PATH(fpath))
        return -1;

    if (fsal != NULL)
    {
        return fsal->closedir(idx);
    }
    return -1;
}

static int FsalIfMkFs(const char *source, char *fstype, uint32_t flags)
{
    fsal_t *fsal;

    if (!source || !fstype)
        return -1;
    if (DiskSoltFindByPath(source) < 0)
    {
        KPrint(PRINT_ERR "[%s]: source %s no device!\n", __func__, source);
        return -1;
    }
    // is auto type
    if (!strcmp("auto", fstype))
    {
        fstype = FSAL_FSTYPE_DEFAULT;
    }

    fsal = FsTypeFind(fstype);
    // call fsal func to mkfs
    int ret = fsal->mkfs(source, fstype, flags);
    if (ret < 0)
        return -1;
    return 0;
}

static int FsalIfMkDir(const char *path, mode_t mode)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];

    if (!path)
        return -1;

    // find fsal path
    fpath = FsalPathFind(path, 1);
    if (!fpath)
    {
        KPrint("[fsalif] path %s not find!\n", path);
        return -1;
    }
    // get targe fsal
    fsal = fpath->fsal;
    if (!fsal)
    {
        KPrint("[fsalif] path %s fsal error", path);
        return -1;
    }
    if (!fsal->mkdir)
        return -ENOSYS;
    // chanage path to new path
    if (!FsalPathSwitch(fpath, new, (char *)path))
    {
        return fsal->mkdir(new, mode);
    }
    return -1;
}

static int FsalIfRmDir(const char *path)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];

    if (!path)
        return -1;

    // find fsal path
    fpath = FsalPathFind(path, 1);
    if (!fpath)
    {
        KPrint("[fsalif] path %s not find!\n", path);
        return -1;
    }
    // get targe fsal
    fsal = fpath->fsal;
    if (!fsal)
    {
        KPrint("[fsalif] path %s fsal error", path);
        return -1;
    }
    if (!fsal->rmdir)
        return -ENOSYS;
    // chanage path to new path
    if (!FsalPathSwitch(fpath, new, (char *)path))
    {
        KPrint("[fsalif] rmdir path %s\n", new);
        return fsal->rmdir(new);
    }
    return -1;
}

static int FsalIfReName(const char *old, const char *new)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char old_path[MAX_PATH_LEN+1], new_path[MAX_PATH_LEN+1];

    if (!old || !new)
        return -1;

    fpath = FsalPathFind(old, 1);
    if (!fpath)
    {
        KPrint("[fsal] path %s no find!\n", old);
        return -1;
    }
    fsal = fpath->fsal;
    if (!fsal)
    {
        KPrint("[fsal] path %s no fsal", old);
        return -1;
    }
    if (FsalPathSwitch(fpath, old_path, (char *)old) < 0)
        return -1;
    if (FsalPathSwitch(fpath, new_path, (char *)new) < 0)
        return -1;
    if (!fsal->rename)
        return -ENOSYS;
    return fsal->rename(old_path, new_path);
}

static int FsalIfUnLink(const char *path)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];

    if (!path)
        return -1;
    // find fsal path
    fpath = FsalPathFind(path, 1);
    if (!fpath)
    {
        KPrint("path %s no found!\n", path);
        return -1;
    }
    // get fsal
    fsal = fpath->fsal;
    if (!fsal)
    {
        KPrint("path %s fsal failed!\n", path);
    }
    if (!fsal->unlink)
        return -ENOSYS;
    // chanage path to new
    if (FsalPathSwitch(fpath, new, (char *)path))
        return -1;
    return fsal->unlink(new);
}

static int FsalIfStatus(const char *path, status_t *buff)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];

    if (!path)
        return -1;
    KPrint("[fsalif] fstat: path %s\n",path);
    fpath = FsalPathFind(path, 1);
    if (!fpath)
    {
        KPrint("path %s no find!\n", path);
        return -1;
    }
    fsal = fpath->fsal;
    if (!fsal)
    {
        KPrint("path %s fsal failed!\n", path);
        return -1;
    }
    if (FsalPathSwitch(fpath, new, (char *)path) < 0)
        return -1;
    if (!fsal->status)
        return -ENOSYS;
    return fsal->status(new, buff);
}

static int FsalIfRead(int idx, void *buff, size_t size)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;

    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;

    fsal = fp->fsal;
    if (!fsal)
    {
        return -1;
    }

    if (!fsal->read)
    {
        return -ENOSYS;
    }

    return fsal->read(idx, buff, size);
}

static int FsalIfWrite(int idx, void *buff, size_t size)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;

    if (!fsal)
        return -1;
    if (!fsal->write)
        return -ENOSYS;

    return fsal->write(idx, buff, size);
}

static int FsalIfIoctl(int idx, int cmd, void *arg)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;

    if (!fsal)
        return -1;
    if (!fsal->ioctl)
        return -ENOSYS;

    return fsal->ioctl(idx, cmd, arg);
}

static int FsalIfFastRead(int idx, void *buff, size_t size)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;
    if (!fsal)
        return -1;
    if (!fsal->fastread)
        return -ENOSYS;
    return fsal->fastread(idx, buff, size);
}

static int FsalIfFastWrite(int idx, void *buff, size_t size)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;
    if (!fsal)
        return -1;
    if (!fsal->fastwrite)
        return -ENOSYS;
    return fsal->fastwrite(idx, buff, size);
}

static int FsalIfChmod(const char *path, mode_t mode)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];

    if (!path)
        return -1;
    fpath = FsalPathFind(path, 1);
    if (!fpath)
        return -1;
    fsal = fpath->fsal;
    if (!fsal)
        return -1;
    if (!fsal->chmod)
        return -ENOSYS;
    if (FsalPathSwitch(fpath, new, (char *)path) < 0)
        return -1;
    return fsal->chmod(new, mode);
}




static int FsalIfAccess(const char *path, mode_t mode)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];

    if (!path)
        return -1;
    fpath = FsalPathFind(path, 1);
    if (!fpath)
        return -1;
    fsal = fpath->fsal;
    if (!fsal)
        return -1;
    if (!fsal->access)
        return -ENOSYS;
    if (FsalPathSwitch(fpath, new, (char *)path) < 0)
        return -1;
    return fsal->access(new, mode);
}

static int FsalIfFChmod(int idx, mode_t mode)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;
    if (!fsal)
        return -1;
    if (!fsal->fchmod)
        return -ENOSYS;
    return fsal->fchmod(idx, mode);
}

static int FsalIfFEof(int idx)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;
    if (!fsal)
        return -1;
    if (!fsal->feof)
        return -ENOSYS;
    return fsal->feof(idx);
}


static int FsalIfFTell(int idx)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;
    if (!fsal)
        return -1;
    if (!fsal->ftell)
        return -ENOSYS;
    return fsal->ftell(idx);
}

static int FsalIfFError(int idx)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;
    if (!fsal)
        return -1;
    if (!fsal->ferror)
        return -ENOSYS;
    return fsal->ferror(idx);
}

static int FsalIfRewind(int idx)
{
    fsal_file_t *fp;
    fsal_t *fsal;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fp = FSAL_FILE_IDX2FILE(idx);
    if (!fp)
        return -1;
    fsal = fp->fsal;
    if (!fsal)
        return -1;
    if (!fsal->rewind)
        return -ENOSYS;
    return fsal->rewind(idx);
}

static int FsalIfMount(const char *source, char *targe, char *fstype, uint32_t flags)
{
    fsal_t *fsal;

    if (!source || !targe || !fstype)
        return -1;

    // check if is device
    if (DiskSoltFindByPath(source) < 0)
    {
        KPrint(PRINT_ERR "[mount]:source %s no found or no is devfs!\n", source);
        return -1;
    }

    if (FsalPathFind(targe, 0) != 0)
    {
        KPrint("[mount]:targe %s had mount!\n", targe);
        return -1;
    }

    // if auto then use default fstype
    if (!strcmp("auto", fstype))
    {
        fstype = FSAL_FSTYPE_DEFAULT;
    }

    fsal = FsTypeFind(fstype);
    if (!fsal)
    {
        KPrint("[mount] no found fstype %s\n", fstype);
        return -1;
    }

    return fsal->mount(source, targe, fstype, flags);
}

static int FsalIfUnMount(char *path, uint32_t flags)
{
    fsal_path_t *fpath;
    fsal_t *fsal;
    char new[MAX_PATH_LEN+1];

    if (!path)
        return -1;
    fpath = FsalPathFind(path, 0);
    if (!fpath)
        return -1;
    fsal = fpath->fsal;
    if (!fsal)
        return -1;
    if (FsalPathSwitch(fpath, new, path) < 0)
        return -1;
    return fsal->unmount(path, flags);
}

static int FsalIfFastIo(int idx, int cmd, void *arg)
{
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    fsal_file_t *file = FSAL_FILE_IDX2FILE(idx);
    fsal_t *fsal = file->fsal;
    if (!fsal)
        return -1;
    if (!fsal->fastio)
        return -ENOSYS;
    return fsal->fastio(idx, cmd, arg);
}

static int FsalIfReadDir(int idx, dirent_t *buff)
{
    if (FSAL_BAD_DIR_IDX(idx))
        return -1;

    fsal_dir_t *dir = FSAL_IDX2DIR(idx);
    fsal_t *fsal = dir->fsal;
    if (!fsal)
        return -1;
    if (!fsal->readdir)
        return -ENOSYS;
    return fsal->readdir(idx, buff);
}

static int FsalIfRewindDir(int idx)
{
    if (FSAL_BAD_DIR_IDX(idx))
        return -1;

    fsal_dir_t *dir = FSAL_IDX2DIR(idx);
    fsal_t *fsal = dir->fsal;
    if (!fsal)
        return -1;
    if (!fsal->rewinddir)
        return -ENOSYS;
    return fsal->rewinddir(idx);
}

fsal_t fsif = {
    .name = "fsif",
    .subtable = NULL,
    .mkfs = FsalIfMkFs,
    .mount = FsalIfMount,
    .unmount = FsalIfUnMount,
    .unlink = FsalIfUnLink,
    .open = FsalIfOpen,
    .close = FsalIfClose,
    .read = FsalIfRead,
    .write = FsalIfWrite,
    .ioctl = FsalIfIoctl,
    .fastio = FsalIfFastIo,
    .fastread = FsalIfFastRead,
    .fastwrite = FsalIfFastWrite,
    .rename = FsalIfReName,
    .lseek = FsalIfSeek,
    .opendir = FsalIfOpenDir,
    .closedir = FsalIfCloseDir,
    .mkdir = FsalIfMkDir,
    .rmdir = FsalIfRmDir,
    .readdir = FsalIfReadDir,
    .rewinddir = FsalIfRewindDir,
    .rewind = FsalIfRewind,
    .fsize = FsalIfFsize,
    .fsync = FsalIfFsync,
    .ftell = FsalIfFTell,
    .incref = FsalIfIncRef,
    .decref = FsalIfDecRef,
    .status = FsalIfStatus,
    .chmod = FsalIfChmod,
    .fchmod = FsalIfFChmod,
    .feof = FsalIfFEof,
    .access = FsalIfAccess,
    .error = FsalIfFError,
};
